/***************************************************************************
 *   Copyright (C) 2009 by Marian Skvarek   *
 *   skvarek.marian@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A TrackICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "CMusicXmlExporter.h"
#include "CScore.h"
#include "CPart.h"
#include "CTrack.h"
#include "CBar.h"
#include "CNote.h"
#include "CPause.h"
#include "CGlobals.h"
#include "CUtils.h"

#include <QtXml/QDomDocument>
#include <QFile>
#include <QTextStream>
#include <QLinkedList>

using namespace easyscore::core;
using namespace easyscore::utils;
using namespace easyscore::globals;

namespace easyscore
{

    namespace components
    {

        namespace io
        {

            //Constructors, Initialization & Destructors

            CMusicXmlExporter::CMusicXmlExporter()
            {

            }

            void CMusicXmlExporter::initialize ()
            {

            }

            void CMusicXmlExporter::clear()
            {

            }

            CMusicXmlExporter::~CMusicXmlExporter()
            {

            }

            bool CMusicXmlExporter::exportScore(CScore * score,QFile &file)
            {
                QDomDocument doc;
                //QString errorStr;
                //int errorLine;
                //int errorColumn;

                //doc.setContent(&file, true, &errorStr, &errorLine, &errorColumn);

                QDomProcessingInstruction instr = doc.createProcessingInstruction("xml", "version='1.0' encoding='UTF-8'");
                doc.appendChild(instr);
                QDomDocumentType dtd;
                QDomElement scoreEl = doc.createElement("score-partwise");
                doc.appendChild(scoreEl);
                scoreEl.setAttribute("version","2.0");

                QDomElement titleElement = doc.createElement("movement-title");
                QDomText titleText = doc.createTextNode(score->getSongname());
                titleElement.appendChild(titleText);
                scoreEl.appendChild(titleElement);

                QDomElement idElement = doc.createElement("identification");

                QDomElement creatorElement = doc.createElement("creator");
                creatorElement.setAttribute("type","composer");
                QDomText authorText = doc.createTextNode(score->getAuthorName());
                creatorElement.appendChild(authorText);
                idElement.appendChild(creatorElement);

                QDomElement encodingElement = doc.createElement("encoding");
                QDomElement softwareElement = doc.createElement("software");
                QDomText softwareText = doc.createTextNode(CGlobals::getFullAppName());
                softwareElement.appendChild(softwareText);
                encodingElement.appendChild(softwareElement);

                idElement.appendChild(encodingElement);

                scoreEl.appendChild(idElement);

                QDomElement partListEl = doc.createElement("part-list");
                scoreEl.appendChild(partListEl);

                int i = 1;
                foreach(CPart *part, score->getParts())
                {
                    QDomElement partDecEl = doc.createElement("score-part");
                    partListEl.appendChild(partDecEl);
                    partDecEl.setAttribute("id","P" + QString::number(i));

                    QDomElement partNameEl = doc.createElement("part-name");
                    partDecEl.appendChild(partNameEl);

                    QDomText partNameText = doc.createTextNode(part->getName());
                    partNameEl.appendChild(partNameText);

                    QDomElement partEl = doc.createElement("part");
                    scoreEl.appendChild(partEl);
                    partEl.setAttribute("id","P" + QString::number(i));

                    int barCount = part->getTracks().first()->getBars().count();
                    QLinkedList<QLinkedList<CBar *>::iterator> bars;
                    foreach(CTrack *track, part->getTracks())
                    {
                        bars.append(track->getBars().begin());
                    }

                    for(int j = 0; j < barCount; ++j)
                    {
                        QDomElement barEl = doc.createElement("measure");
                        partEl.appendChild(barEl);
                        barEl.setAttribute("number",j + 1);

                        foreach(QLinkedList<CBar *>::iterator it, bars)
                        {
                            foreach(CSymbol *symbol, (*it)->getSymbols())
                            {

                                if(symbol->isRest())
                                {
                                    QDomElement symbolEl = doc.createElement("note");
                                    barEl.appendChild(symbolEl);

                                    QDomElement noteTypeEl = doc.createElement("type");
                                    symbolEl.appendChild(noteTypeEl);

                                    QDomText noteTypeText = doc.createTextNode(getSymbolValueText(symbol->getValue()));
                                    noteTypeEl.appendChild(noteTypeText);

                                    QDomElement restEl = doc.createElement("rest");
                                    symbolEl.appendChild(restEl);
                                }
                                else
                                {
                                    CNoteGroup *noteGroup = (CNoteGroup *) symbol;

                                    bool firstNote = true;
                                    foreach(CNote *note, noteGroup->getNotes())
                                    {

                                        QDomElement symbolEl = doc.createElement("note");
                                        barEl.appendChild(symbolEl);

                                        QDomElement noteTypeEl = doc.createElement("type");
                                        symbolEl.appendChild(noteTypeEl);

                                        QDomText noteTypeText = doc.createTextNode(getSymbolValueText(symbol->getValue()));
                                        noteTypeEl.appendChild(noteTypeText);

                                        QDomElement pitchEl = doc.createElement("pitch");
                                        symbolEl.appendChild(pitchEl);

                                        QDomElement stepEl = doc.createElement("step");
                                        pitchEl.appendChild(stepEl);

                                        QDomText stepElText = doc.createTextNode(getNoteStepText(note->getRelativePitch()));
                                        stepEl.appendChild(stepElText);

                                        QDomElement octaveEl = doc.createElement("octave");
                                        pitchEl.appendChild(octaveEl);

                                        QDomText octaveElText = doc.createTextNode(QString::number(note->getOctave() - 1));
                                        octaveEl.appendChild(octaveElText);

                                        if(!firstNote)
                                        {
                                            QDomElement chordEl = doc.createElement("chord");
                                            symbolEl.appendChild(chordEl);
                                        }

                                        firstNote = false;

                                    }
                                }

                            }

                            ++it;
                        }
                    }


                    ++i;
                }

                //try to open the file
                if(!file.open( QIODevice::WriteOnly ))
                    return false;

                //write to file
                QTextStream out(&file);
                out << doc.toString();
                file.close();

                return true;
            }

            //Protected methods
            QString CMusicXmlExporter::getSymbolValueText(NOTE_VALUE value)
            {
                switch(value)
                {
                case NOTE_VALUE_LONGA: return "long";
                case NOTE_VALUE_DOUBLE_WHOLE: return "breve";
                case NOTE_VALUE_WHOLE: return "whole";
                case NOTE_VALUE_HALF: return "half";
                case NOTE_VALUE_QUARTER: return "quarter";
                case NOTE_VALUE_EIGHTH: return "eighth";
                case NOTE_VALUE_SIXTEENTH: return "16th";
                case NOTE_VALUE_THIRTYSECOND: return "32nd";
                case NOTE_VALUE_SIXTYFOURTH: return "64th";
                case NOTE_VALUE_HUNDREDTWENTYEIGTH: return "128th";
                default: throw "Unknown note type";
                }


            }

            QString CMusicXmlExporter::getNoteStepText(NOTE_PITCH pitch)
            {
                switch(pitch)
                {
                case NOTE_PITCH_C: return "C";
                case NOTE_PITCH_D: return "D";
                case NOTE_PITCH_E: return "E";
                case NOTE_PITCH_F: return "F";
                case NOTE_PITCH_G: return "G";
                case NOTE_PITCH_A: return "A";
                case NOTE_PITCH_H: return "B"; //TODO
                default: throw "Unknown note pitch";
                }
            }

        }

    }

}
